﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Needle.DevToolkit.Traits;
using Needle.Utils;
using Needle.Utils.Parser;

 namespace Needle.DevToolkit.Workspace
{
    public class SolutionFileParser
    {
        private string _FilePath;
        public SolutionFileParser(string filePath)
            : this(File.OpenText(filePath))
        {
            _FilePath = filePath;
        }
        public SolutionFileParser(TextReader reader)
        {
            Ensure.NotNull(reader, "reader");
            _Reader = reader;
        }
        private TextReader _Reader;
        public ISolution Parse()
        {
            char[] punctuators = new char[] { '(', ')', '=', '\t', ',' };
            using (StringTokenizer tokenizer = new StringTokenizer(_Reader, punctuators))
            {
                IEnumerator<StringToken> tokens = tokenizer.GetEnumerator();
                MoveToNextToken(tokens);
                ISolution solution = ReadHeader(tokens);
                MoveToNextToken(tokens);
                ReadProjects(tokens, solution);
                MoveToToken("Global", tokens);
                ReadGlobal(tokens, solution);
                solution.FilePath = _FilePath;
                if (_FilePath != null)
                {
                    FileInfo info = new FileInfo(_FilePath);
                    solution.Name = info.Name.Remove(info.Name.LastIndexOf(info.Extension), info.Extension.Length);
                }
                return solution;
            }
        }
        private ISolution ReadHeader(IEnumerator<StringToken> tokens )
        {
            MoveToToken("Version", tokens);
            MoveToNextToken(tokens);
            Version version = new Version(tokens.Current.Value);
            Solution solution = new Solution();
            solution.Version = version;
            MoveToNextLine(tokens);
            MoveToNextToken(tokens);
            solution.ProductName = ReadToNext(TokenType.NewLine, tokens).Trim();
            return solution;
        }
        private void ReadProjects(IEnumerator<StringToken> tokens, IParent parent)
        {
            while (tokens.Current.Value == "Project")
            {
                Project project = new Project();
                parent.Items.Add(project);
                MoveToNextToken(tokens);
                project.TypeGuid = new Guid(ReadToNext(TokenType.Punctuator, tokens).Trim(' ', '"'));
                MoveToNextToken(tokens);
                project.Name = ReadToNext(TokenType.Punctuator, tokens).Trim(' ', '"');
                MoveToNextToken(tokens);
                project.Path = ReadToNext(TokenType.Punctuator, tokens).Trim(' ', '"');
                MoveToNextToken(tokens);
                project.Guid = new Guid(ReadToNext(TokenType.NewLine, tokens).Trim(' ', '"'));
                MoveToNextToken(tokens);
                ReadSections<ProjectSection>(tokens, project);
                MoveToNextToken(tokens);
            }
        }
        private void ReadGlobal(IEnumerator<StringToken> tokens, IParent parent)
        {
            GlobalSections global = new GlobalSections();
            parent.Items.Add(global);
            MoveToToken("GlobalSection", tokens);
            ReadSections<GlobalSection>(tokens, global);
        }
        private void ReadSections<TSection>(IEnumerator<StringToken> tokens, IParent parent)
            where TSection : ISection, new()
        {
            string sectionName = typeof(TSection).Name;
            while (tokens.Current.Value == sectionName)
            {
                TSection section = new TSection();
                parent.Items.Add(section);
                MoveToNextToken(tokens);
                section.Name = tokens.Current.Value;
                MoveToNextToken(tokens);
                section.Order = tokens.Current.Value;
                MoveToNextLine(tokens);
                ReadKeyValuePairs(tokens, section);
                MoveToNextToken(tokens);
            }
        }
        private void ReadKeyValuePairs(IEnumerator<StringToken> tokens, ISection section)
        {
            String endSection = "End" + section.GetType().Name;
            MoveToNextToken(tokens);
            while (tokens.Current.Value != endSection)
            {
                String key = ReadToNext(TokenType.Punctuator, tokens).Trim();
                MoveToNextToken(tokens);
                String value = ReadToNext(TokenType.NewLine, tokens).Trim();
                section.Items.Add(key, value);
                MoveToNextToken(tokens);
            }
        }
        private void MoveToNextLine(IEnumerator<StringToken> tokens)
        {
            if (tokens.Current != null && tokens.Current.Type != TokenType.NewLine)
            {
                while (tokens.MoveNext() && tokens.Current.Type != TokenType.NewLine)
                { }
            }
            tokens.MoveNext();
        }
        private void MoveToNextToken(IEnumerator<StringToken> tokens)
        {
            while (tokens.MoveNext() && tokens.Current.Type != TokenType.Token)
            { }
        }
        private void MoveToToken(string value, IEnumerator<StringToken> tokens)
        {
            if (tokens.Current != null && tokens.Current.Value == value)
                return;
            while (tokens.MoveNext()&& tokens.Current.Value != value )
            { }
        }
        private string ReadToNext(TokenType tokenType, IEnumerator<StringToken> tokens)
        {
            StringBuilder buffer = new StringBuilder();
            while (tokens.Current.Type != tokenType)
            {
                buffer.Append(tokens.Current.Value);
                tokens.MoveNext();
            }
            return buffer.ToString();
        }
    }
}
